#ifndef CLIENT_H_
#define CLIENT_H_

#include <sys/queue.h>
#include <ev.h>
#include "socks.h"
#include "http.h"
#include "config.h"
#include "resolv.h"
#include "misc.h"
#include "crypt.h"


#define REQ_CHUNK_LEN   BUF_SIZE_MIN // NOTE: must large than sizeof(struct request.socks)

struct client;
struct client_ctx {
    const struct cfg *cfg;
    struct resolv_ctx *resolver;
    int listen_sock;
    struct ev_loop *evloop;
    ev_io evio;
    struct {
        TAILQ_HEAD(inuse, client) inuse;
        TAILQ_HEAD(unuse, client) unuse;
        size_t unuseTotal;
    } client_pool;
    unsigned rand_seed;
    void *inbuf;
    size_t inbuflen;
    void *outbuf;
    size_t outbuflen;
};

enum src_proto {
    SP_SOCKS4,
    SP_SOCKS4a,
    SP_SOCKS5,
    SP_HTTP,
    SP_INVALID,
};

struct client {
    TAILQ_ENTRY(client) entry;
    struct client_ctx *ctx;

    const struct server_info *srv;
    int lsock;
    int rsock;
    struct fifo *lout;
    struct fifo *rout;
    char laddrinfo[ADDRSTRLEN];
    char raddrinfo[ADDRSTRLEN];
    ev_io levio;
    ev_io revio;
    ev_timer evtimer;
    struct crypt crypt;
    struct resolv *resolv;
    resolv_cb *resolv_cb;

    enum src_proto src_proto;
    char dst_info[256+1+5]; // URL + ":" + "port"

    struct request *req;
};

struct request {
    int stat; // parse status
    union {
        struct socks socks; // socks info
        struct http http; // http info
    };

    struct {
        size_t size; // buffer size
        size_t len; // data length
        char *pos;
        union {
            char raw[0]; // buffer data

            // socks
            union {
                unsigned char ver; /* socks version code */
                struct socks4 s4;
                struct socks5 s5;
            } socks;
        };
    } buf;
};

const char *src_proto_str(enum src_proto p);
struct request *request_realloc(struct request *old);
void client_update_activity(struct client *c);
ssize_t client_write2sock(int fd, const void *data, size_t nbytes, struct client *c);
void client_log_socket_error(int loglevel, const struct client *c, int fd, const char *dowhat);
int client_write_event(EV_P_ ev_io *w, struct client *c);
void relay_handler(EV_P_ ev_io *w, int revents);
void client_destroy(struct client *c);

struct client_ctx *client_ctx_create(const struct cfg *cfg, struct resolv_ctx *resolv, struct ev_loop *evloop);
int client_ctx_destroy(struct client_ctx *cctx);
void update_server_weight(struct client *c, int n);

void sweep_handler(EV_P_ ev_timer *watcher, int revents);

#endif /* CLIENT_H_ */
